#ifndef F_CPU
#define F_CPU 1000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "i2cmaster.h"
#include "luart.h"
//#include "uart.h"




#define USART_BUFFER_SIZE 128
#define INPUT_BUFFER_SIZE 128
#define I2C_BUFFER_SIZE 128
#define DELAY_MS 1000

unsigned char address;
unsigned char reg;

int state;
int how_many_bytes;
char DEVICE_ADDR = 0b1001101;
char DATA_REGISTER = 0b0;
char i2c_data;


unsigned char i2c_check_addr(unsigned char dev_addr);
unsigned char i2c_read_byte (unsigned char dev_addr, unsigned char reg);
unsigned int str_to_hex(const char* str);
unsigned char str_to_bin(const char* str);
void execute(const char* command);


char usart_data[USART_BUFFER_SIZE];
char input_buffer[INPUT_BUFFER_SIZE];
unsigned char i2c_buffer[I2C_BUFFER_SIZE];


ISR(USART_RXC_vect)
{
    char character = UDR;

    if (character == UART_NEWLINE)
    {
        execute(input_buffer);
        input_buffer[0] = '\0';
    }
    else if (character == UART_BACKSPACE)
	{
		input_buffer[strlen(input_buffer) - 1] = '\0';
	}
	else		
    {
        // can be optimized
        int oldlen = strlen(input_buffer);
        input_buffer[oldlen++] = character;
        input_buffer[oldlen] = '\0';
    }
}

int main(void)
{
	state = 0;
	
    i2c_init();
    usart_init();
	
	_delay_ms(DELAY_MS);
	
	//usart_println("Hello!");
	
	sei();
        
    while (1)
    {
        _delay_ms(DELAY_MS);
    } 
}

// 0 if a device with such address exists, 1 if it does not exist
unsigned char i2c_check_addr(unsigned char dev_addr)
{
    return i2c_start((dev_addr << 1) + I2C_WRITE);
}

// should be optimized
unsigned char i2c_read_byte(unsigned char dev_addr, unsigned char reg)
{
    unsigned char result;
        
    // start writing
    i2c_start((dev_addr << 1) + I2C_WRITE); // we're going to write
    i2c_write(reg); // selecting a register
    // stop writing
        
    // start reading
    i2c_rep_start((dev_addr << 1) + I2C_READ); // we're going to read
    result = i2c_readNak();
    i2c_stop();
    // stop reading
                
    return result;
}

unsigned char* i2c_read_bytes(unsigned char dev_addr, unsigned char reg, int bytes_qnt, unsigned char* buffer)
{
    // start writing
    i2c_start((dev_addr << 1) + I2C_WRITE); // we're going to write
    i2c_write(reg); // selecting a register
    // stop writing
        
    // start reading
    i2c_rep_start((dev_addr << 1) + I2C_READ); // we're going to read
	
	for (int i = 0; i < bytes_qnt - 1; i++)
	{
		buffer[i] = i2c_readAck();
	}		
	buffer[bytes_qnt - 1] = i2c_readNak();
	
    i2c_stop();
    // stop reading
                
    return buffer;
}

unsigned int str_to_hex(const char* str)
{
    unsigned int res = 0;
        
    sscanf(str, "%x", &res);
        
    return res;
}

unsigned char str_to_bin(const char* str)
{
	unsigned char r = 0;
	
    for (int i = 0; str[i] != '\0'; i++)
    {
        r = r * 2 + str[i] - '0';
    }
	
	return r;
}

void execute(const char* command)
{
	unsigned char number = str_to_bin(command);

	if (state == 0)
	{
		address = number;
		sprintf(usart_data, "Your input is equal to: %i.", number);
		usart_println(usart_data);
		sprintf(usart_data, "I2C answer is: %i.", i2c_check_addr(number));
		usart_println(usart_data);
	}
	else if (state == 1)
	{
		how_many_bytes = atoi(command);
		sprintf(usart_data, "%i bytes will be read.", how_many_bytes);
		usart_println(usart_data);
	}
	else if (state == 2)
	{
		reg = number;
		sprintf(usart_data, "Your input is equal to: %i.", number);
		usart_println(usart_data);
		if (how_many_bytes == 1)
		{
			sprintf(usart_data, "Register %i (address %i) content is: %i.", reg, address, i2c_read_byte(address, reg));	
			usart_println(usart_data);
		}
		else
		{
			sprintf(usart_data, "Register %i (address %i) content is:", reg, address);
			usart_println(usart_data);
			i2c_read_bytes(address, reg, how_many_bytes, i2c_buffer);
			
			for (int i = 0; i < how_many_bytes; i++)
			{
				sprintf(usart_data, "%i", i2c_buffer[i]);
				usart_println(usart_data);
			}
		}
	}
	
	state++;
	
	if (state == 3)
	{
		state = 0;
	}
}
